<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Model extends CI_Model
{
	protected $_table = "";

	public function __construct()
	{
		parent::__construct();
	}

	public function find($condition = null, $order_by = null, $limit = 0, $offset = 0)
	{
		if($condition)
		{
			$this->db->where($condition);
		}

		if($order_by)
		{
			$this->db->order_by($order_by);
		}

		if($limit)
		{
			$this->db->limit($limit, $offset);
		}

		$result = $this->db->get($this->_table);

		return $result;
	}

	public function count($condition = null)
	{
		if($condition)
		{
			$this->db->where($condition);
		}

		$this->db->from($this->_table);

		return $this->db->count_all_results();
	}

	public function find_id($id)
	{
		$this->db->where('id', $id);
		$result = $this->db->get($this->_table);

		return $result;
	}

	public function insert($data)
	{
		$this->db->insert($this->_table, $data);

		return $this->db->insert_id();
	}

	public function insert_ignore($data, $update = array())
	{
		$fields = join(', ', array_keys($data));
		$values = join("', '", array_values($data));
		$sql = "
			INSERT IGNORE INTO {$this->_table} ({$fields}) 
			VALUES ('{$values}')
		";

		if($update)
		{
			foreach($update as $key => $val)
			{
				$tmp[] = "{$key}='{$val}'";
			}
			$set = join(', ', $tmp);
			$sql.= "
				ON DUPLICATE KEY UPDATE {$set};
			";
		}

		$this->db->query($sql);

		return $this->db->insert_id();
	}

	public function save($data)
	{
		$this->db->where($data);
		$query = $this->db->get($this->_table);

		if($query->num_rows())
		{
			return $query->row()->id;
		}
		else
		{
			$data['created'] = date('Y-m-d H:i:s');
			$this->db->insert($this->_table, $data);
			
			return $this->db->insert_id();
		}
	}

	public function update($condition, $data)
	{
		$this->db->where($condition);
		$this->db->update($this->_table, $data);

		return $this->db->affected_rows();
	}

	public function delete($condition)
	{
		$this->db->where($condition);
		$this->db->delete($this->_table);

		return $this->db->affected_rows();
	}

	public function list_fields()
	{
		return $this->db->list_fields($this->_table);
	}
}

/**
 * An implementation of Joe Celko's Nested Sets as a Code Igniter model.
 * Ideally, this would be an abstract class since it should be extended
 * by another class to provide specific functionality (ie a Categories class
 * or a Site_structure class). However, PHP4.x doesn't have support for
 * asbtract classes so I've done it this way for now.
 *
 * @package     Nested_sets
 * @author      Thunder <ravenvelvet@gmail.com>
 * @copyright   Copyright (c) 2007, Thunder
 */


/**
 * @package Nested_sets
 * @author  Thunder <ravenvelvet@gmail.com>
 * @version 1.0
 * @copyright Copyright (c) 2007 Thunder
 */
class Nested_sets_model extends MY_Model
{
	protected $lft;
	protected $rgt;
	protected $pk;

	/**
	 * Constructor
	 *
	 * @access	public
	 */
	public function __construct()
	{
		// Call the parent constructor
		parent::__construct();
	}

	// -------------------------------------------------------------------------
	//  OBJECT INITIALISATION METHODS
	//
	//  For setting instance properties
	//
	// -------------------------------------------------------------------------

	/**
	 *  On initialising the instance, this method should be called to set the
	 *  database table name that we're dealing and also to identify the names
	 *  of the left and right value columns used to form the tree structure.
	 *  Typically, this would be done automatically by the model class that
	 *  extends this "base" class (eg. a Categories class would set the table_name
	 *  to "categories", a Site_structure class would set the table_name to
	 *  "pages" etc)
	 *
	 *  @param string $table_name The name of the db table to use
	 *  @param string $lft The name of the field representing the left identifier
	 *  @param string $rgt The name of the field representing the right identifier
	 *  @return void
	 *
	 */
	function setControlParams($table_name,$lft = "lft",$rgt = "rgt")
	{
		$this->_table = $table_name;
		$this->lft = $lft;
		$this->rgt = $rgt;
		return "";
	}

	/**
	 * Used to identify the primary key of the table in use. Commonly, this will
	 * be an auto_incrementing ID column (eg CategoryId)
	 *
	 * @param string $primary_key_name
	 * @return void
	 */
	function setPrimaryKeyColumn($primary_key_name)
	{
		$this->pk = $primary_key_name;
	}


	// -------------------------------------------------------------------------
	//  NODE MANIPULATION FUNCTIONS
	//
	//  Methods to add/remove nodes in your tree
	//
	// -------------------------------------------------------------------------


	/**
	 * Adds the first entry to the table
	 * @param     $extrafields  An array of field->value pairs for the database record
	 * @return    $node an array of left and right values
	 */

	function initialiseRoot($extrafields = array())
	{

		$node = array(
			$this->lft  => 1,
			$this->rgt => 2
		);
			
		$this->_setNewNode($node, $extrafields);

		return $node;
	}
	/**
	 * inserts a new node as the first child of the supplied parent node
	 * @param array $parentNode The node array of the parent to use
	 * @param array $extrafields An associative array of fieldname=>value for the other fields in the recordset
	 * @return array $childNode An associative array representing the new node
	 */
	function insertNewChild($parentNode, $extrafields = array())
	{
		$childNode[$this->lft]	 = $parentNode[$this->lft] + 1;
		$childNode[$this->rgt] = $parentNode[$this->lft] + 2;

		$this->_modifyNode($childNode[$this->lft], 2);
		$this->_setNewNode($childNode, $extrafields);

		$childNode = array_merge($childNode, $extrafields);

		return $childNode;
	}

	/**
	 * Same as insertNewChild except the new node is added as the last child
	 * @param array $parentNode The node array of the parent to use
	 * @param array $extrafields An associative array of fieldname=>value for the other fields in the recordset
	 * @return array $childNode An associative array representing the new node
	 */
	function appendNewChild($parentNode, $extrafields = array())
	{
		$childNode[$this->lft]      =   $parentNode[$this->rgt];
		$childNode[$this->rgt]     =   $parentNode[$this->rgt]+1;

		$this->_modifyNode($childNode[$this->lft], 2);
		$this->_setNewNode($childNode, $extrafields);

		$childNode = array_merge($childNode, $extrafields);
		return $childNode;
	}
	/**
	 * Adds a new node to the left of the supplied focusNode
	 * @param array $focusNode The node to use as the position marker
	 * @param array $extrafields An associative array of node attributes
	 * @return array $siblingNode The new node
	 */
	function insertSibling($focusNode, $extrafields)
	{
		$siblingNode[$this->lft]    =   $focusNode[$this->lft];
		$siblingNode[$this->rgt]   =   $focusNode[$this->lft]+1;

		$this->_modifyNode($siblingNode[$this->lft], 2);
		$this->_setNewNode($siblingNode, $extrafields);

		$siblingNode = array_merge($siblingNode, $extrafields);
		return $siblingNode;
	}

	/**
	 * Adds a new node to the right of the supplied focusNode
	 * @param array $focusNode The node to use as the position marker
	 * @param array $extrafields An associative array of node attributes
	 * @return array $siblingNode The New Node
	 */
	function appendSibling($focusNode, $extrafields)
	{
		$siblingNode[$this->lft]    =   $focusNode[$this->rgt]+1;
		$siblingNode[$this->rgt]   =   $focusNode[$this->rgt]+2;

		$this->_modifyNode($siblingNode[$this->lft], 2);
		$this->_setNewNode($siblingNode, $extrafields);

		return $siblingNode;
	}


	/**
	 * Empties the table currently in use - use with extreme caution!
	 */
	function deleteTree()
	{
		$sql = "DELETE FROM " . $this->_table;
		$res = $this->db->query($sql);

		return;
	}

	/**
	 * Deletes the given node (and any children) from the tree table
	 * 
	 * @param array $node The node to remove from the tree
	 * @return array $newnode The node that replaced the deleted node
	 */
	function deleteNode($node)
	{
		$leftanchor         =       $node[$this->lft];
		$table              =       $this->_table;
		$leftcol            =       $this->lft;
		$rightcol           =       $this->rgt;
		$leftval            =       $node[$this->lft];
		$rightval           =       $node[$this->rgt];

		$sql = "DELETE FROM     $table
                WHERE           $leftcol    >= $leftval 
                AND             $rightcol   <= $rightval";

		$this->db->query($sql);

		$this->_modifyNode($node[$this->rgt]+1, $node[$this->lft] -$node[$this->rgt] - 1);

		return $this->getNodeWhere("$leftcol < $leftanchor ORDER BY $leftcol DESC");
	}

	// -------------------------------------------------------------------------
	//  MODIFY/REORGANISE TREE
	//
	//  Methods to move nodes around the tree. Method names should be
	//  relatively self-explanatory! Hopefully ;)
	//
	// -------------------------------------------------------------------------

	/**
	 * Moves the given node to make it the next sibling of "target"
	 * @param array $node The node to move
	 * @param array $target The node to use as the position marker
	 * @return array $newpos The new left and right values of the node moved
	 */
	function setNodeAsNextSibling($node, $target)
	{
		return $this->_moveSubtree($node, $target[$this->rgt]+1);
	}

	/**
	 * Moves the given node to make it the prior sibling of "target"
	 * @param array $node The node to move
	 * @param array $target The node to use as the position marker
	 * @return array $newpos The new left and right values of the node moved
	 */
	function setNodeAsPrevSibling($node, $target)
	{
		return $this->_moveSubtree($node, $target[$this->lft]);
	}

	/**
	 * Moves the given node to make it the first child of "target"
	 * @param array $node The node to move
	 * @param array $target The node to use as the position marker
	 * @return array $newpos The new left and right values of the node moved
	 */
	function setNodeAsFirstChild($node, $target)
	{
		return $this->_moveSubtree($node, $target[$this->lft]+1);
	}

	/**
	 * Moves the given node to make it the last child of "target"
	 * @param array $node The node to move
	 * @param array $target The node to use as the position marker
	 * @return array $newpos The new left and right values of the node moved
	 */
	function setNodeAsLastChild($node, $target)
	{
		return $this->_moveSubtree($node, $target[$this->rgt]);
	}

	// -------------------------------------------------------------------------
	//  QUERY METHODS
	//
	//  Selecting nodes from the tree
	//
	// -------------------------------------------------------------------------

	/**
	 * Selects the first node to match the given where clause argument
	 * @param string $whereArg Any valid SQL to follow the WHERE keyword in an SQL statement
	 * @return array $resultNode The node returned from the query
	 */
	function getNodeWhere($whereArg = "1=1")
	{
		$resultNode[$this->lft] =       $resultNode[$this->rgt]    =       0;
		$leftcol                =       $this->lft;
		$rightcol               =       $this->rgt;
		$table                  =       $this->_table;

		$sql = "SELECT      *
                FROM        $table
                WHERE       $whereArg";

		$query = $this->db->query($sql);

		if($query->num_rows() > 0)
		{
			$result = $query->result_array();
			$resultNode = array_shift($result); // assumes CI standard $row[0] = first row
		}

		return $resultNode;
	}

	/**
	 * Returns the node identified by the given left value
	 * @param integer $leftval The left value to use to select the node
	 * @return array $resultNode The node returned
	 */
	function getNodeWhereLeft($leftval)
	{
		return $this->getNodeWhere($this->lft . " = " . $leftval);
	}

	/**
	 * Returns the node identified by the given right value
	 * @param integer $rightval The right value to use to select the node
	 * @return array $resultNode The node returned
	 */
	function getNodeWhereRight($rightval)
	{
		return $this->getNodeWhere($this->rgt . " = " . $rightval);
	}

	/**
	 * Returns the root node
	 * @return array $resultNode The node returned
	 */
	function getRoot()
	{
		return $this->getNodeWhere($this->lft . " = 1 ");
	}

	/**
	 * Returns the node with the appropriate primary key field value.
	 * Typically, this will be an auto_incrementing primary key column
	 * such as categoryid
	 * @param mixed $primarykey The value to look up in the primary key index
	 * @return array $resultNode The node returned
	 */
	function getNodeFromId($primarykey)
	{
		// Test if we've set the primary key column name property
		if(empty($this->pk)) return false;

		return $this->getNodeWhere($this->pk . "='$primarykey'");
	}

	/**
	 * Returns the first child node of the given parentNode
	 * @param array $parentNode The parent node to use
	 * @return array $resultNode The first child of the parent node supplied
	 */
	function getFirstChild($parentNode)
	{
		return $this->getNodeWhere($this->lft . " = " . ($parentNode[$this->lft]+1));
	}

	/**
	 * Returns the last child node of the given parentNode
	 * @param array $parentNode The parent node to use
	 * @return array $resultNode the last child of the parent node supplied
	 */
	function getLastChild($parentNode)
	{
		return $this->getNodeWhere($this->rgt . " = " . ($parentNode[$this->rgt]-1));
	}

	/**
	 * Returns the node that is the immediately prior sibling of the given node
	 * @param array $currNode The node to use as the initial focus of enquiry
	 * @return array $resultNode The node returned
	 */
	function getPrevSibling($currNode)
	{
		return $this->getNodeWhere($this->rgt . " = " . ($currNode[$this->lft]-1));
	}

	/**
	 * Returns the node that is the next sibling of the given node
	 * @param array $currNode The node to use as the initial focus of enquiry
	 * @return array $resultNode The node returned
	 */
	function getNextSibling($currNode)
	{
		return $this->getNodeWhere($this->lft . " = " . ($currNode[$this->rgt]+1));
	}

	/**
	 * Returns the node that represents the parent of the given node
	 * @param array $currNode The node to use as the initial focus of enquiry
	 * @return array $resultNode the node returned
	 */
	function getAncestor($currNode)
	{
		$leftcol        =       $this->lft;
		$rightcol       =       $this->rgt;

		$whereArg = "           $leftcol    < " . $currNode[$leftcol] .
                    " AND       $rightcol   > " . $currNode[$rightcol] . 
                    " ORDER BY  $rightcol ASC";
		return $this->getNodeWhere($whereArg);
	}


	// -------------------------------------------------------------------------
	//  NODE TEST METHODS
	//
	//  Boolean tests for nodes
	//
	// -------------------------------------------------------------------------


	/**
	 * Returns true or false
	 * (in reality, it checks to see if the given left and
	 * right values _appear_ to be valid not necessarily that they _are_ valid)
	 * @param array $node The node to test
	 * @return boolean
	 */
	function checkIsValidNode($node)
	{
		return ($node[$this->lft] < $node[$this->rgt]);
	}

	/**
	 * Tests whether the given node has an ancestor
	 * (effectively the opposite of isRoot yes|no)
	 * @param array $node The node to test
	 * @return boolean
	 */
	function checkNodeHasAncestor($node)
	{
		return $this->checkIsValidNode(getAncestor($node));
	}

	/**
	 * Tests whether the given node has a prior sibling or not
	 * @param array $node
	 * @return boolean
	 */
	function checkNodeHasPrevSibling($node)
	{
		return $this->checkIsValidNode(getPrevSibling($node));
	}

	/**
	 * Test to see if node has siblings after itself
	 * @param array $node The node to test
	 * @return boolean
	 */
	function checkNodeHasNextSibling($node)
	{
		return $this->CheckIsValidNode(getNextSibling($node));
	}

	/**
	 * Test to see if node has children
	 * @param array $node The node to test
	 * @return boolean
	 */
	function checkNodeHasChildren($node)
	{
		return (($node[$this->rgt] - $node[$this->lft]) > 1);
	}

	/**
	 * Test to see if the given node is also the root node
	 * @param array $node The node to test
	 * @return boolean
	 */
	function checkNodeIsRoot($node)
	{
		return ($node[$this->lft] == 1);
	}

	/**
	 * Test to see if the given node is a leaf node (ie has no children)
	 * @param array $node The node to test
	 * @return boolean
	 */
	function checkNodeIsLeaf($node)
	{
		return (($node[$this->rgt] - $node[$this->lft]) == 1);
	}

	/**
	 * Test to see if the first given node is a child of the second given node
	 * @param array $testNode the node to test for child status
	 * @param array $controlNode the node to use as the parent or ancestor
	 * @return boolean
	 */
	function checkNodeIsChild($testNode, $controlNode)
	{
		return (
		($testNode[$this->lft] > $controlNode[$this->lft])
		&&  ($testNode[$this->rgt] < $controlNode[$this->rgt])
		);
	}

	/**
	 * Test to determine whether testNode is infact also controlNode (is A === B)
	 * @param array $testNode The node to test
	 * @param array $controlNode The node prototype to use for the comparison
	 * @return boolean
	 */
	function checkNodeIsEqual($testNode, $controlNode)
	{
		return (($testNode[$this->lft]==$controlNode[$this->lft]) and ($testNode[$this->rgt]==$controlNode[$this->rgt]));
	}

	/**
	 * Combination method of IsChild and IsEqual
	 *
	 * @param array $testNode The node to test
	 * @param array $controlNode The node prototype to use for the comparison
	 * @return boolean
	 */
	function checkNodeIsChildOrEqual($testNode, $controlNode)
	{
		return (($testNode[$this->lft]>=$controlNode[$this->lft]) and ($testNode[$this->rgt]<=$controlNode[$this->rgt]));
	}


	// ------------------------------------------------------------------------- //
	//  TREE QUERY METHODS														 //
	//  Query the tree itself													 //
	// ------------------------------------------------------------------------- //

	/**
	 * Returns the number of descendents that a node has
	 *
	 * @param array $node The node to query
	 * @return integer The number of descendents
	 */
	function getNumberOfChildren($node)
	{
		return (($node[$this->rgt] - $node[$this->lft] - 1) / 2);
	}

	/**
	 * Returns the tree level for the given node (assuming root node is at level 0)
	 *
	 * @param array $node The node to query
	 * @return integer The level of the supplied node
	 */
	function getNodeLevel($node)
	{
		$leftcol        =       $this->lft;
		$rightcol       =       $this->rgt;
		$table          =       $this->_table;
		$leftval        = (int) $node[$this->lft];
		$rightval       = (int) $node[$this->rgt];

		$sql = "
			SELECT      COUNT(*) AS level
            FROM        $table  
            WHERE       $leftcol < $leftval 
            AND         $rightcol > $rightval
        ";

		$query = $this->db->query($sql);
		if($query->num_rows() > 0) {
			$result = $query->result();
			return $result->level;
		} else {
			return 0;
		}
	}

	/**
	 * Returns an array of the tree starting from the supplied node
	 *
	 * @param array $node The node to use as the starting point (typically root)
	 * @return array $tree_handle The tree represented as an array to assist with
	 *                            the other tree traversal operations
	 */
	function getTreePreorder($node)
	{
		$table      =       $this->_table;
		$leftcol    =       $this->lft;
		$rightcol   =       $this->rgt;
		$leftval    = (int) $node[$leftcol];
		$rightval   = (int) $node[$rightcol];

		$sql = "SELECT      *
                FROM        $table
                WHERE       $leftcol >= $leftval
                AND         $rightcol <= $rightval
                ORDER BY    $leftcol ASC";

		$query = $this->db->query($sql);

		$treeArray = array();

		if($query->num_rows() > 0) {
			foreach($query->result_array() AS $result) {
				$treeArray[] = $result;
			}
		}

		$retArray = array(  "result_array"  =>      $treeArray,
                            "prev_left"     =>      $node[$leftcol],
                            "prev_right"    =>      $node[$rightcol],
                            "level"         =>      -2);

		return $retArray;
	}

	/**
	 * Returns the next element from the tree and updates the tree_handle with the
	 * new positions
	 * @param array $tree_handle Passed by reference to allow for modifications
	 * @return array The next node in the tree
	 */
	function getTreeNext(&$tree_handle)
	{
		$leftcol        =       $this->lft;
		$rightcol       =       $this->rgt;

		if(!empty($tree_handle['result_array'])) {
			if($row = array_shift($tree_handle['result_array'])) {

				$tree_handle['level']+= $tree_handle['prev_left'] - $row[$leftcol] + 2;
				// store current node
				$tree_handle['prev_left']  = $row[$leftcol];
				$tree_handle['prev_right'] = $row[$rightcol];
				$tree_handle['row'] = $row;

				return array(
				$leftcol  =>  $row[$leftcol],
				$rightcol =>  $row[$rightcol],
				);
			}
		}

		return FALSE;
	}

	/**
	 * Returns the given attribute (database field) for the current node in $tree_handle
	 * @param array $tree_handle The tree as an array
	 * @param string $attribute A string containing the fieldname to retrieve
	 * @return string The value requested
	 */
	function getTreeAttribute($tree_handle,$attribute)
	{
		return $tree_handle['row'][$attribute];
	}

	/**
	 * Returns the current node of the tree contained in $tree_handle
	 * @param array $tree_handle The tree as an array
	 * @return array The left and right values of the current node
	 */
	function getTreeCurrent($tree_handle)
	{
		return array(
		$this->lft  =>      $tree_handle['prev_left'],
		$this->rgt =>      $tree_handle['prev_right'],
		);
	}
	/**
	 * Returns the current level from the tree
	 * @param array $tree_handle The tree as an array
	 * @return integer The integer value of the current level
	 */
	function getTreeLevel($tree_handle)
	{
		return $tree_handle['level'];
	}


	// -------------------------------------------------------------------------
	//   NODE FIELD QUERIES
	//
	// -------------------------------------------------------------------------

	/**
	 * Queries the database for the value of the given field
	 *
	 * Thanks to CI user hkk for poiting out a bug in this method. Fixed.
	 *
	 * @param array $node The node to be queried
	 * @param string $fieldname The name of the field to query
	 * @return string $retval The value of the field for the node looked up
	 */
	function getNodeAttribute($node, $fieldname)
	{
		$table          =       $this->_table;
		$leftcol        =       $this->lft;
		$rightcol       =       $this->rgt;
		$leftval        = (int) $node[$this->lft];

		$sql = "SELECT      *
                FROM        $table
                WHERE       $leftcol = $leftval";

		$query = $this->db->query($sql);


		if($query->num_rows() > 0) {
			$res = $query->row();
			return $res->{$fieldname};
		} else {
			return "";
		}
	}

	/**
	 * Renders the fields for each node starting at the given node
	 * @param array $node The node to start with
	 * @param array $fields The fields to display for each node
	 * @return string Sample HTML render of tree
	 */
	function getSubTreeAsHTML($node, $fields = array())
	{
		$tree_handle = $this->getTreePreorder($node);
		$retVal = "";

		while($this->getTreeNext($tree_handle))
		{
			// print indentation
			$retVal .= (str_repeat("&nbsp;", $this->getTreeLevel($tree_handle)*4));

			// print requested fields
			$field = reset($fields);
			while($field){
				$retVal .= $tree_handle['row'][$field] . "\n";
				$field = next($fields);
			}
			$retVal .= "<br />\n";

		}

		return $retVal;
	}


	/**
	 * Renders the entire tree as per getSubTreeAsHTML starting from root
	 * @param array $fields An array of the fields to display
	 */
	function getTreeAsHTML($fields=array())
	{
		return $this->getSubTreeAsHTML($this->getRoot(), $fields);
	}

	// -------------------------------------------------------------------------
	//  INTERNALS
	//
	//  Private, internal methods
	//
	// -------------------------------------------------------------------------

	/**
	 *  _setNewNode
	 *
	 *  Inserts a new node into the tree
	 *
	 *  @param array $node An array containing the left and right values to use
	 *  @param array $extrafields An associative array of field names to values for \
	 *                          additional columns in tree table (eg CategoryName etc)
	 *
	 *  @return boolean True/False dependent upon the success of the operation
	 *  @access private
	 */
	private function _setNewNode($node, $extrafields)
	{
		$table      =       $this->_table;
		$leftcol    =       $this->lft;
		$rightcol   =       $this->rgt;
		$leftval    = (int) $node[$this->lft];
		$rightval   = (int) $node[$this->rgt];

		// Handle 'othercols'
		$extraFieldsArg = $extraValuesArg = "";

		if(is_array($extrafields) && !empty($extrafields)) {
			$fields = array();
			$values = array();

			foreach($extrafields AS $field=>$value) {
				$fields[] = $field;
				$values[] = $value;
			}
			$extraFieldsArg = ",`" . join("`,`",$fields) . "`";
			$extraValuesArg = ",'" . join("','",$values) . "'";
		}

		$sql = "INSERT INTO $table (
                    `$leftcol`,
                    `$rightcol`
                    $extraFieldsArg)
                 VALUES (
                    '$leftval',
                    '$rightval'
                    $extraValuesArg
                 )";

                    if($this->db->query($sql)) {
                    	return true;
                    } else {
                    	log_message('error', "Node addition failed for $left_value - $right_value");
                    }
                     
                    return false;
	}


	/**
	 * The method that performs moving/renumbering operations
	 * @param array $node The node to move
	 * @param array $targetValue Position integer to use as the target
	 * @return array $newpos The new left and right values of the node moved
	 * @access private
	 */
	private function _moveSubtree($node, $targetValue)
	{
		$sizeOfTree = $node[$this->rgt] - $node[$this->lft] + 1;
		$this->_modifyNode($targetValue, $sizeOfTree);


		if($node[$this->lft] >= $targetValue)
		{
			$node[$this->lft] += $sizeOfTree;
			$node[$this->rgt] += $sizeOfTree;
		}

		$newpos = $this->_modifyNodeRange($node[$this->lft], $node[$this->rgt], $targetValue - $node[$this->lft]);

		$this->_modifyNode($node[$this->rgt]+1, - $sizeOfTree);

		if($node[$this->lft] <= $targetValue)
		{
			$newpos[$this->lft] -= $sizeOfTree;
			$newpos[$this->rgt] -= $sizeOfTree;
		}

		return $newpos;
	}

	/**
	 * _modifyNode
	 *
	 * Adds $changeVal to all left and right values that are greater than or
	 * equal to $node_int
	 *
	 * @param  $node_int The value to start the shift from
	 * @param  $changeVal unsigned integer value for change
	 * @access private
	 */
	private function _modifyNode($node_int, $changeVal)
	{
		$leftcol        =       $this->lft;
		$rightcol       =       $this->rgt;
		$table          =       $this->_table;

		$sql =  "UPDATE     $table " .
                "SET        $leftcol = $leftcol + $changeVal ".
                "WHERE      $leftcol >= $node_int";

		$this->db->query($sql);

		$sql =  "UPDATE     $table " .
                "SET        $rightcol = $rightcol + $changeVal ".
                "WHERE      $rightcol >= $node_int";

		$this->db->query($sql);

		return true;
	} // END: _modifyNode

	/**
	 * _modifyNodeRange
	 *
	 * @param $lowerbound integer value of lowerbound of range to move
	 * @param $upperbound integer value of upperbound of range to move
	 * @param $changeVal unsigned integer of change amount
	 * @access private
	 */

	private function _modifyNodeRange($lowerbound, $upperbound, $changeVal)
	{
		$leftcol        =       $this->lft;
		$rightcol       =       $this->rgt;
		$table          =       $this->_table;

		$sql = "UPDATE      $table
                SET         $leftcol    =   $leftcol    +   $changeVal 
                WHERE       $leftcol    >=  $lowerbound  
                AND         $leftcol    <=  $upperbound";

		$this->db->query($sql);

		$sql = "UPDATE      $table
                SET         $rightcol   =   $rightcol   +   $changeVal
                WHERE       $rightcol   >=  $lowerbound
                AND         $rightcol   <=  $upperbound";

		$this->db->query($sql);

		$retArray = array(
		$this->lft  =>  $lowerbound+$changeVal,
		$this->rgt =>  $upperbound+$changeVal
		);
		return $retArray;
	} // END: Method _modifyNodeRange

} // END: Class Nested_sets
/* End of file MY_Model.php */
/* Location: ./system/application/libraries/MY_Model.php */